import numpy as np
import pandas as pd
import os
from nicegui import ui
import plotly.express as px
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import time

# 数据集配置
DATA_CONFIG = {
    'fundamentals': {
        'local_file': 'D:/大数据编程课设/nyse-data/fundamentals.csv',
        'description': '公司基本面数据'
    },
    'prices': {
        'local_file': 'D:/大数据编程课设/nyse-data/prices.csv',
        'description': '股票价格数据'
    },
    'prices_split_adjusted': {
        'local_file': 'D:/大数据编程课设/nyse-data/prices-split-adjusted.csv',
        'description': '拆分调整后的股票价格数据'
    },
    'securities': {
        'local_file': 'D:/大数据编程课设/nyse-data/securities.csv',
        'description': '证券基本信息'
    }
}


def load_dataset(dataset_name):
    """加载数据集"""
    config = DATA_CONFIG[dataset_name]

    try:
        if os.path.exists(config['local_file']):
            df = pd.read_csv(config['local_file'])
            ui.notify(f"成功加载数据集: {config['description']}", type='positive')
            return df
        else:
            ui.notify(f"文件不存在: {config['local_file']}", type='negative')
            return None
    except Exception as e:
        ui.notify(f"加载{config['description']}失败: {str(e)}", type='negative')
        return None


def preprocess_data(df, dataset_name):
    """数据预处理"""
    if df is None or df.empty:
        return None

    print(f"\n=== 预处理 {DATA_CONFIG[dataset_name]['description']} ===")
    print("数据基本信息：")
    df.info()
    print(f"数据行数: {df.shape[0]}, 列数: {df.shape[1]}")

    # 处理缺失值
    missing_cols = df.columns[df.isnull().any()].tolist()
    for col in missing_cols:
        if df[col].dtype in [np.float64, np.int64]:
            # 数值类型用中位数填充
            median_val = df[col].median()
            df[col] = df[col].fillna(median_val)
            print(f"填充 {col} 列缺失值，中位数: {median_val}")
        else:
            # 分类类型用众数填充
            mode_val = df[col].mode()[0] if not df[col].mode().empty else "未知"
            df[col] = df[col].fillna(mode_val)
            print(f"填充 {col} 列缺失值，众数: {mode_val}")

    # 保存预处理后的数据
    preprocessed_file = DATA_CONFIG[dataset_name]['local_file'].replace('.csv', '_preprocessed.csv')
    df.to_csv(preprocessed_file, index=False)
    ui.notify(f"预处理完成，已保存至: {preprocessed_file}", type='positive')

    return df


def create_visualizations(df, dataset_name):
    """创建可视化图表"""
    if df is None or df.empty:
        return {}

    print(f"\n=== 为 {DATA_CONFIG[dataset_name]['description']} 创建可视化 ===")

    if dataset_name == 'securities':
        # 证券基本信息可视化
        fig1 = px.histogram(
            df,
            x='market_cap',
            nbins=50,
            log_y=True,
            title='公司市值分布',
            labels={'market_cap': '市值(美元)', 'count': '公司数量'},
            hover_data=['symbol', 'name', 'sector']
        )
        fig1.update_layout(xaxis=dict(tickformat='$.2s'))

        fig2 = px.pie(
            df,
            names='sector',
            title='行业分布',
            labels={'sector': '行业', 'count': '公司数量'},
            hole=0.3
        )

        return {
            'market_cap': fig1,
            'sector_distribution': fig2
        }

    elif dataset_name in ['prices', 'prices_split_adjusted']:
        # 股票价格数据可视化
        if 'date' in df.columns and 'close' in df.columns:
            # 转换日期格式（指定正确的格式）
            df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d')

            # 按日期排序
            df = df.sort_values('date')

            # 绘制价格趋势图
            fig1 = px.line(
                df,
                x='date',
                y='close',
                color='symbol' if 'symbol' in df.columns else None,
                title='股票价格趋势',
                labels={'date': '日期', 'close': '收盘价(美元)'}
            )

            # 成交量分布
            fig2 = px.histogram(
                df,
                x='volume',
                nbins=50,
                log_y=True,
                title='成交量分布',
                labels={'volume': '成交量', 'count': '交易次数'}
            )

            return {
                'price_trend': fig1,
                'volume_distribution': fig2
            }

        elif 'close' in df.columns:
            # 只有收盘价的简单分布
            fig = px.histogram(
                df,
                x='close',
                nbins=50,
                title='收盘价分布',
                labels={'close': '收盘价(美元)', 'count': '交易次数'}
            )
            return {'price_distribution': fig}

    elif dataset_name == 'fundamentals':
        # 公司基本面数据可视化
        # 计算总资产回报率 ROA = 净利润 / 总资产
        if 'Net Income' in df.columns and 'Total Assets' in df.columns:
            # 处理净利润为负的情况
            df['Net Income Abs'] = df['Net Income'].abs()  # 创建绝对值列

            df['ROA'] = df['Net Income'] / df['Total Assets'] * 100  # 转换为百分比

            # 筛选有效数据（总资产>0）
            valid_data = df[df['Total Assets'] > 0]

            # 总资产回报率与净利润关系（使用绝对值作为气泡大小）
            fig = px.scatter(
                valid_data,
                x='Total Assets',
                y='ROA',
                color='Ticker Symbol' if 'Ticker Symbol' in valid_data.columns else None,
                size='Net Income Abs',
                title='总资产回报率与净利润关系',
                labels={
                    'Total Assets': '总资产(美元)',
                    'ROA': '总资产回报率(%)',
                    'Net Income Abs': '净利润绝对值'
                },
                log_x=True,
                hover_data=['Ticker Symbol', 'For Year', 'Net Income'] if 'For Year' in valid_data.columns else [
                    'Ticker Symbol', 'Net Income']
            )
            fig.update_layout(
                xaxis=dict(tickformat='$.2s'),
                yaxis=dict(title='总资产回报率 (%)'),
                hoverlabel=dict(
                    bgcolor="white",
                    font_size=12,
                    font_family="Arial"
                )
            )

            # 备用可视化 - 净利润分布（使用绝对值）
            fig_net_income = px.histogram(
                df,
                x='Net Income',
                nbins=50,
                log_y=True,
                title='净利润分布',
                labels={'Net Income': '净利润(美元)', 'count': '公司数量'}
            )
            fig_net_income.update_layout(xaxis=dict(tickformat='$.2s'))

            return {
                'roa_assets': fig,
                'net_income_dist': fig_net_income
            }

        # 备用可视化 - 净利润分布
        if 'Net Income' in df.columns:
            fig = px.histogram(
                df,
                x='Net Income',
                nbins=50,
                log_y=True,
                title='净利润分布',
                labels={'Net Income': '净利润(美元)', 'count': '公司数量'}
            )
            fig.update_layout(xaxis=dict(tickformat='$.2s'))
            return {'net_income_dist': fig}

    # 默认返回空图表
    return {}


def build_ui():
    """构建数据分析Web界面"""
    current_df = None
    current_dataset = None
    all_figures = {}

    with ui.header().classes('bg-blue-600 text-white shadow-md'):
        with ui.row().classes('w-full justify-between items-center'):
            ui.label('纽约证券交易所数据分析平台').classes('text-2xl font-bold')
            ui.button('刷新页面', on_click=lambda: ui.open(ui.page.path)).classes('text-white')

    with ui.row().classes('w-full h-screen'):
        # 左侧控制面板
        with ui.column().classes('w-1/4 p-4 bg-gray-50 shadow-lg'):
            ui.label('数据控制面板').classes('text-xl font-bold mb-4 text-blue-700')

            # 数据集选择
            ui.label('选择数据集:').classes('font-medium')
            dataset_select = ui.select(
                options=list(DATA_CONFIG.keys()),
                value=list(DATA_CONFIG.keys())[0],
                label='数据集',
                on_change=lambda e: load_selected_dataset(e.value)
            )

            # 图表类型选择
            ui.label('选择可视化图表:').classes('font-medium mt-6')
            chart_select = ui.select(
                options=[],
                value=None,
                label='图表类型',
                on_change=lambda e: update_chart(e.value)
            )

            # 数据统计信息
            ui.label('数据统计:').classes('font-medium mt-6')
            stats_display = ui.label('请先加载数据').classes('text-sm text-gray-600')

            # 数据预览
            ui.label('数据预览:').classes('font-medium mt-6')
            data_preview = ui.table(
                columns=[],
                rows=[],
                row_key='index',
                pagination={'rows-per-page': 5}
            ).classes('max-h-60 overflow-auto')

        # 右侧可视化展示区
        with ui.column().classes('w-3/4 p-4'):
            # 图表标题
            chart_title = ui.label('纽约证券交易所数据可视化').classes('text-xl font-bold mb-4 text-blue-700')

            # Plotly图表容器
            plotly_container = ui.plotly(go.Figure()).classes('w-full h-[70vh]')

            # 图表说明
            ui.label('图表说明:').classes('font-medium mt-4')
            chart_description = ui.label('选择图表查看详细说明').classes('text-gray-700')

        # 定义所有内部函数
        # 更新图表函数
        def update_chart(chart_type):
            if not all_figures or chart_type is None:
                plotly_container.update_figure(go.Figure())
                chart_description.text = '请先加载数据并选择图表类型'
                return

            if chart_type in all_figures:
                plotly_container.update_figure(all_figures[chart_type])
                chart_title.text = {
                    'market_cap': '公司市值分布直方图',
                    'sector_distribution': '行业分布饼图',
                    'price_trend': '股票价格趋势图',
                    'volume_distribution': '成交量分布直方图',
                    'price_distribution': '收盘价分布直方图',
                    'roa_assets': '总资产回报率与净利润关系散点图',
                    'net_income_dist': '净利润分布直方图'
                }.get(chart_type, '数据可视化')

                chart_description.text = {
                    'market_cap': '展示不同市值区间的公司数量分布，使用对数Y轴以更好展示长尾分布',
                    'sector_distribution': '展示各行业在纽约证券交易所的公司数量占比',
                    'price_trend': '展示股票价格随时间的变化趋势',
                    'volume_distribution': '展示不同成交量区间的交易次数分布',
                    'price_distribution': '展示收盘价的分布情况',
                    'roa_assets': '分析公司总资产回报率与净利润的关系，气泡大小表示净利润绝对值',
                    'net_income_dist': '展示公司净利润的分布情况'
                }.get(chart_type, '请选择图表查看说明')
            else:
                chart_description.text = '未找到该图表类型'

        # 更新数据预览
        def update_preview(df):
            if df is not None and not df.empty:
                data_preview.columns = [{"name": i, "label": i, "field": i} for i in df.columns[:10]]  # 限制显示列数
                data_preview.rows = df.head(10).to_dict('records')
                data_preview.visible = True
                data_preview.update()

                # 更新图表选项
                chart_select.options = list(all_figures.keys())
                chart_select.value = list(all_figures.keys())[0] if all_figures else None
                chart_select.update()
            else:
                data_preview.visible = False
                chart_select.options = []
                chart_select.value = None
                chart_select.update()

        # 更新数据统计信息
        def update_stats():
            if current_df is not None and not current_df.empty:
                stats = [
                    f"数据集: {DATA_CONFIG[current_dataset]['description']}",
                    f"数据行数: {current_df.shape[0]}",
                    f"数据列数: {current_df.shape[1]}",
                ]

                # 添加数据集中存在的列的统计信息
                if 'Ticker Symbol' in current_df.columns:
                    stats.append(f"公司数量: {current_df['Ticker Symbol'].nunique()}")

                if 'For Year' in current_df.columns:
                    stats.append(
                        f"数据年份范围: {int(current_df['For Year'].min())} 至 {int(current_df['For Year'].max())}")

                # 根据数据集类型添加特定统计信息
                if current_dataset == 'securities' and 'market_cap' in current_df.columns:
                    stats.extend([
                        f"平均市值: ${current_df['market_cap'].mean():,.2f}",
                        f"最高市值: ${current_df['market_cap'].max():,.2f}",
                        f"最低市值: ${current_df['market_cap'].min():,.2f}",
                        f"行业数量: {current_df['sector'].nunique()}"
                    ])
                elif current_dataset in ['prices', 'prices_split_adjusted'] and 'close' in current_df.columns:
                    stats.extend([
                        f"平均收盘价: ${current_df['close'].mean():,.2f}",
                        f"最高收盘价: ${current_df['close'].max():,.2f}",
                        f"最低收盘价: ${current_df['close'].min():,.2f}",
                        f"日期范围: {current_df['date'].min().strftime('%Y-%m-%d')} 至 {current_df['date'].max().strftime('%Y-%m-%d')}"
                    ])
                elif current_dataset == 'fundamentals':
                    if 'Net Income' in current_df.columns:
                        stats.extend([
                            f"平均净利润: ${current_df['Net Income'].mean():,.2f}",
                            f"最高净利润: ${current_df['Net Income'].max():,.2f}",
                            f"最低净利润: ${current_df['Net Income'].min():,.2f}",
                            f"净利润为正的公司占比: {(current_df['Net Income'] > 0).mean() * 100:.2f}%"
                        ])

                    if 'Total Assets' in current_df.columns:
                        stats.extend([
                            f"平均总资产: ${current_df['Total Assets'].mean():,.2f}",
                            f"最高总资产: ${current_df['Total Assets'].max():,.2f}",
                            f"最低总资产: ${current_df['Total Assets'].min():,.2f}",
                        ])

                    # 如果计算了ROA
                    if 'ROA' in current_df.columns:
                        stats.append(f"平均总资产回报率: {current_df['ROA'].mean():.2f}%")

                stats_display.text = "\n".join([s for s in stats if s])
            else:
                stats_display.text = '数据加载失败，请检查文件路径'

        # 加载选中的数据集
        def load_selected_dataset(dataset_name):
            nonlocal current_df, current_dataset, all_figures
            current_dataset = dataset_name
            current_df = load_dataset(dataset_name)

            if current_df is not None:
                current_df = preprocess_data(current_df, dataset_name)
                all_figures = create_visualizations(current_df, dataset_name)

                # 更新界面
                update_stats()
                update_chart(list(all_figures.keys())[0] if all_figures else None)
                update_preview(current_df)
            else:
                stats_display.text = '数据加载失败，请检查文件路径'
                chart_title.text = '数据可视化'
                chart_description.text = '请选择数据集并成功加载后查看图表'
                update_preview(None)

        # 初始加载默认数据集
        load_selected_dataset(dataset_select.value)


# 启动界面
if __name__ in {"__main__", "__mp_main__"}:
    build_ui()
    ui.run(title='纽约证券交易所数据分析平台', port=8080)